package base_classes;
import grid.Couple;
import grid.Direction;
import grid.GridModel;
import grid.ShootingGridModel;
import grid.items.Body;

import java.awt.Point;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import parameters.StaticParameters;

import connexion.EnterMessage;
import connexion.IntegerMessage;
import connexion.Message;
import connexion.PlacementMessage;
import connexion.StringMessage;

public class World {
	
	public static final double SHOOTING_DISTANCE = 5;
	ShootingGridModel gm;
	//private static final double DISTANCE_SENSING = 7;	
	HashMap<String,HashMap<String,Body>>body=
		new HashMap<String,HashMap<String,Body>>();
	
//	int nbAgent=0;
	
	
	Queue<Couple<Body,Integer>>move=
		new LinkedList<Couple<Body,Integer>>();
	Set<Body>registeredMove=new HashSet<Body>();
	
	Queue<Couple<Body,Point>> shot=
		new LinkedList<Couple<Body,Point>>();
	Set<Body>registeredShot=new HashSet<Body>();
		
	public World(int x, int y)
	{
		gm=new ShootingGridModel(x, y);
	}
	
	public void requestMove(String bodyName,String team,int direction)
	{
		
		if(!existsBody(team,bodyName))
			{
			if(StaticParameters.WORLD_ERRORS_VERBOSITY)
				System.out.println(bodyName+" does not exists (dead or not created)");
			return;
			}
		Body b=getBody(bodyName,team);
		if(registeredMove.contains(bodyName))
		{
			if(StaticParameters.WORLD_ERRORS_VERBOSITY)
				System.out.println("Agent "+b+" has already requested a move this round");
			return;
		}
		else
				move.add(new Couple<Body, Integer>(b,direction));
	}
	
	private Body getBody(String bodyName, String team) {
		return body.get(team).get(bodyName);
	}

	private boolean existsBody(String team, String bodyName) {
		if(body.containsKey(team))
			if(body.get(team).containsKey(bodyName))
				return true;
		return false;
	}

	public void requestShot(String bodyName,String team,Point p)
	{
		if(!existsBody(team, bodyName))
		{
			if(StaticParameters.WORLD_ERRORS_VERBOSITY)
			System.out.println(bodyName+" "+team+" does not exist but tries to shoot");
			return;
		}
		
		Body b=getBody(bodyName, team);
		if(registeredShot.contains(bodyName))
		{
			if(StaticParameters.WORLD_ERRORS_VERBOSITY)
				System.out.println("Agent "+b+" has already requested a shot this round");
			return;
		}
		else
		{
			registeredShot.add(b);
			shot.add(new Couple<Body,Point>(b,p));
		}
	}
	
	public void executeMoves()
	{
		registeredMove.clear();
		
		for(Couple<Body,Integer>m:move)
		if(!m.getX().isDead())
			gm.move(m.getX(),m.getY());
		move.clear();
	}
	
	public void executeShots()
	{

		registeredShot.clear();
		
		System.out.println("going to execute shots:" + shot);
		
		Set<Body>killed=gm.executeShots(shot);
		System.out.println("killed: "+killed);
		
		for(Body b:killed)
			body.get(b.getTeam()).remove(b.getName());
		
		shot.clear();
		
	}


	public void enter(Body b, Point p) {
		gm.enter(b,p);
		if(!body.containsKey(b.getTeam()))
			body.put(b.getTeam(),new HashMap<String, Body>());
		body.get(b.getTeam()).put(b.getName(), b);
		
	}
	
	public String toString()
	{
		return gm.toString();
	}

	public int getNumberOfRemainingTeams() {
		int total=0;
		for(Map<String,Body> i:body.values())
			if(i.size()>0)
				total++;
		return total;
			
	}

	public String getWinningTeam() {
		for(Map<String,Body> i:body.values())
			if(i.size()>0)
				return i.values().iterator().next().getTeam();
		throw new Error();
	}

	public void handleMessage(Message m) {
		if(m instanceof EnterMessage)
		{
			EnterMessage pm=(EnterMessage)m;
			if(pm.getReason().equals(EnterMessage.PLACEMENT))
			{
				enter
				(
						new Body
						(
								pm.getAgent(),
								pm.getTeam(),
								pm.getType()),
								pm.getPosition()
				);
			}
			else throw new Error();
		}
		else 
			if(m instanceof IntegerMessage)
			{
				IntegerMessage im=(IntegerMessage)m;
				if(im.getReason().equals(IntegerMessage.DIRECTION))
					requestMove(im.agentConcerned(),im.getTeam(),im.getValue());
				else throw new Error();
			}
			else if(m instanceof PlacementMessage)
			{
				PlacementMessage pm=(PlacementMessage)m;
				if(((PlacementMessage) m).getReason().equals(PlacementMessage.SHOOTING_REASON))
					this.requestShot(((PlacementMessage) m).getAgent(),((PlacementMessage) m).getTeam(),((PlacementMessage) m).getPosition());
				else throw new Error();
			}

			else throw new Error();


		//	System.out.println(m.getClass());
	}

	public HashMap<Body, Map<Body, Point>> senseAll() {
		HashMap<Body, Map<Body, Point>> result=new HashMap<Body, Map<Body,Point>>();
		for(HashMap<String,Body>team:body.values())
			for(Body b:team.values())
			{
					result.put(b,new HashMap<Body, Point>());
					Set<Couple<Body,Point>>s=gm.sense(b);
					for(Couple<Body,Point>c:s)
						result.get(b).put(c.getX(), c.getY());
			}
		return result;
	}

	public Point getPosition(Body b) {
		return gm.getPosition(b);
	}

}
